using System;
using System.Threading;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Input.Touch;
using Microsoft.Xna.Framework.Media;

namespace Zephyris_Space_Mercs
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        bool isHit = false;
        TimeSpan isHitTimeSpan;

        UIPages.UIPage gameState = UIPages.UIPage.MainMenu;
        Song mainMusic;

        GraphicsDeviceManager graphics;
        GraphicsDevice device;
        SpriteBatch spriteBatch;
        SpriteBatch textBatch;
        SpriteBatch uiBatch;

        static PlayerEntity player = new PlayerEntity();
        Controller controller = new Controller();
        ThumbsticksValues thumbsticksVals = new ThumbsticksValues();
        AccelerometerValues accelerometerVals = new AccelerometerValues();

        HUD hud;
        SpriteFont HUDFont;

        StarField[] starFieldArr = new StarField[3];
        Texture2D starsTex;
        float starFieldRange = 1.0f;

        Plane playerPlaneYZ;
        Plane playerPlaneXY;
        Matrix worldMatrix = Matrix.Identity;

        Model model;
        Model skysphere;
        MapDef mapDef;
        Texture2D thumbstick;
        Texture2D reticule;
        Texture2D firebutton;
        Texture2D hitindicator;
        Texture2D valuebar;

        Model shield;
        static Model bullet_Plasma;

        MainMenu mainMenu = new MainMenu();
        AboutMenu aboutMenu = new AboutMenu();
        SettingsMenu settingsMenu = new SettingsMenu();
        SinglePlayerMenu singleplayerMenu = new SinglePlayerMenu();
        MultiPlayerMenu multiplayerMenu = new MultiPlayerMenu();
        LANMultiPlayerMenu lanMultiplayerMenu = new LANMultiPlayerMenu();
        LANHMultiPlayerMenu lanhMultiplayerMenu = new LANHMultiPlayerMenu();
        HangarMenu hangarMenu = new HangarMenu();
        BazaarMenu bazaarMenu = new BazaarMenu();
        TradeMenu tradeMenu = new TradeMenu();

        CollisionDetector collisionDetector = new CollisionDetector();

        List<Map> mapList = new List<Map>();

        static Dictionary<Guid, PlayerEntity> otherPlayers_Table = new Dictionary<Guid, PlayerEntity>();
        static List<BulletObject> bulletsList = new List<BulletObject>();
        
        static PeerNode peerNode;

        public static PeerNode PeerNode
        {
            get { return peerNode; }
            set { peerNode = value; }
        }

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";

            // Frame rate is 30 fps by default for Windows Phone.
            TargetElapsedTime = TimeSpan.FromTicks(333333);
            // TargetElapsedTime = TimeSpan.FromTicks(166666);
        }

        protected override void Initialize()
        {
            // TODO: Add your initialization logic here

            this.graphics.SupportedOrientations = DisplayOrientation.LandscapeLeft;
            this.graphics.ApplyChanges();

            this.graphics.PreferredBackBufferHeight = 600;
            this.graphics.PreferredBackBufferWidth = 800;

            device = graphics.GraphicsDevice;

            RasterizerState rasterizer = new RasterizerState();
            rasterizer.CullMode = CullMode.None;

            device.DepthStencilState = DepthStencilState.Default;
            device.RasterizerState = rasterizer;
           

            Camera.aspectRatio = GraphicsDevice.Viewport.AspectRatio;
            Camera.cameraRot = Quaternion.Identity;

            Camera.view = Matrix.CreateLookAt(new Vector3(20, 13, -5), new Vector3(8, 0, -7), new Vector3(0, 1, 0));
            Camera.projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, device.Viewport.AspectRatio, 0.2f, 500.0f);

            Vector2 leftStick = new Vector2(0, 0);
            leftStick.X = 20;
            leftStick.Y = device.Viewport.Height - 20;

            Vector2 rightStick = new Vector2(0, 0);
            rightStick.X = device.Viewport.Width - 20;
            rightStick.Y = device.Viewport.Height - 20;

            controller.drawLeftThumbstick = true;
            controller.drawRightThumbstick = false;

            spriteBatch = new SpriteBatch(device);
            textBatch = new SpriteBatch(device);
            uiBatch = new SpriteBatch(device);

            mainMenu.Init(spriteBatch, uiBatch, device);
            aboutMenu.Init(spriteBatch, uiBatch, device);
            settingsMenu.Init(spriteBatch, uiBatch, device);
            singleplayerMenu.Init(spriteBatch, uiBatch, device);
            multiplayerMenu.Init(spriteBatch, uiBatch, device);
            lanMultiplayerMenu.Init(spriteBatch, uiBatch, device);
            lanhMultiplayerMenu.Init(spriteBatch, uiBatch, device);
            hangarMenu.Init(spriteBatch, uiBatch, device);

            base.Initialize();
        }

        protected override void LoadContent()
        {
            SpriteFont uiFont = Content.Load<SpriteFont>(Constants.uiFontPath);
            SpriteFont uiFont2 = Content.Load<SpriteFont>(Constants.uiFont2Path);
            Texture2D uiBackground = Content.Load<Texture2D>(Constants.uiBackgroundPath);
            Texture2D uiButton = Content.Load<Texture2D>(Constants.uiButtonPath);

            mainMenu.LoadContent(uiBackground, uiButton, uiFont, uiFont);
            aboutMenu.LoadContent(uiBackground, uiButton, uiFont);
            settingsMenu.LoadContent(uiBackground, uiButton, uiFont);
            singleplayerMenu.LoadContent(uiBackground, uiButton, uiFont);
            multiplayerMenu.LoadContent(uiBackground, uiButton, uiFont);
            lanMultiplayerMenu.LoadContent(uiBackground, uiButton, uiFont);
            lanhMultiplayerMenu.LoadContent(uiBackground, uiButton, uiFont);
            hangarMenu.LoadContent(uiBackground, uiButton, uiFont);

            model = Content.Load<Model>(Constants.modelPath);
            thumbstick = Content.Load<Texture2D>(Constants.thumbstickPath);
            reticule = Content.Load<Texture2D>(Constants.reticulePath);
            firebutton = Content.Load<Texture2D>(Constants.fireButtonPath);
            hitindicator = Content.Load<Texture2D>(Constants.hitindicatorPath);
            valuebar = Content.Load<Texture2D>(Constants.valuebarPath);
            HUDFont = Content.Load<SpriteFont>(Constants.HUDFontPath);
            starsTex = Content.Load<Texture2D>(Constants.starsTex);

            shield = Content.Load<Model>(Constants.shieldPath);
            bullet_Plasma = Content.Load<Model>(Constants.bullet_plasma_Path);

            mainMusic = Content.Load<Song>(Constants.mainMusicPath);

            PopulateMapList();

            LoadMap(0);
            skysphere = mapList[0].map; // Delete in future
            mapDef = mapList[0].mapDef;

            // TODO: use this.Content to load your game content here

            hud = new HUD(HUDFont);
            SetupStarField();

        }

        #region Star Field

        private void SetupStarField()
        {
            StarField starField1 = new StarField();
            StarField starField2 = new StarField();
            StarField starField3 = new StarField();

            starField1.InitStars(device, starsTex);
            starField1.needsReset = true;
            starField2.InitStars(device, starsTex);
            starField2.needsReset = true;
            starField3.InitStars(device, starsTex);
            starField3.needsReset = true;

            starFieldArr[0] = starField1;
            starFieldArr[1] = starField2;
            starFieldArr[2] = starField3;
        }

        private void UpdateStarField()
        {
            if (Math.Abs(player.pos.X - starFieldArr[1].currPos.X) > starFieldRange)     
            {
                starFieldArr[0].needsReset = true;
                starFieldArr[0].currPos = new Vector3(player.pos.X + starFieldRange, player.pos.Y, player.pos.Z);
                starFieldArr[0].CreateStars();

                starFieldArr[1].needsReset = true;
                starFieldArr[1].currPos = player.pos;
                starFieldArr[1].CreateStars();

                starFieldArr[2].needsReset = true;
                starFieldArr[2].currPos = new Vector3(player.pos.X - starFieldRange, player.pos.Y, player.pos.Z);
                starFieldArr[2].CreateStars();
            }

            if (Math.Abs(player.pos.Y - starFieldArr[1].currPos.Y) > starFieldRange)
            {
                starFieldArr[0].needsReset = true;
                starFieldArr[0].currPos = new Vector3(player.pos.X, player.pos.Y + starFieldRange, player.pos.Z);
                starFieldArr[0].CreateStars();

                starFieldArr[1].needsReset = true;
                starFieldArr[1].currPos = player.pos;
                starFieldArr[1].CreateStars();

                starFieldArr[2].needsReset = true;
                starFieldArr[2].currPos = new Vector3(player.pos.X, player.pos.Y - starFieldRange, player.pos.Z);
                starFieldArr[2].CreateStars();
            }

            if (Math.Abs(player.pos.Z - starFieldArr[1].currPos.Z) > starFieldRange)
            {
                starFieldArr[0].needsReset = true;
                starFieldArr[0].currPos = new Vector3(player.pos.X, player.pos.Y, player.pos.Z + starFieldRange);
                starFieldArr[0].CreateStars();

                starFieldArr[1].needsReset = true;
                starFieldArr[1].currPos = player.pos;
                starFieldArr[1].CreateStars();

                starFieldArr[2].needsReset = true;
                starFieldArr[2].currPos = new Vector3(player.pos.X, player.pos.Y, player.pos.Z - starFieldRange);
                starFieldArr[2].CreateStars();
            }

        }

        private void DrawStarField()
        {
            starFieldArr[0].DrawStars(Camera.view, Camera.projection);
            starFieldArr[1].DrawStars(Camera.view, Camera.projection);
            starFieldArr[2].DrawStars(Camera.view, Camera.projection);
        }


        #endregion

        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
            {
                if (gameState != UIPages.UIPage.MainMenu)
                {
                    gameState = UIPages.UIPage.MainMenu;
                }
                else
                {
                    this.Exit();
                }
            }

            switch (gameState)
            {
                case UIPages.UIPage.AboutMenu:
                    gameState = aboutMenu.CheckClick(GetButtonTouch());
                    break;

                case UIPages.UIPage.SettingsMenu:
                    gameState = settingsMenu.CheckClick(GetButtonTouch());
                    break;

                case UIPages.UIPage.SinglePlayerMenu:
                    gameState = singleplayerMenu.CheckClick(GetButtonTouch());
                    break;

                case UIPages.UIPage.MultiPlayerMenu:
                    gameState = multiplayerMenu.CheckClick(GetButtonTouch());
                    break;

                case UIPages.UIPage.LANMultiPlayerMenu:
                    gameState = lanMultiplayerMenu.CheckClick(GetButtonTouch());
                    break;

                case UIPages.UIPage.LANHMultiPlayerMenu:
                    gameState = lanhMultiplayerMenu.CheckClick(GetButtonTouch());
                    break;

                case UIPages.UIPage.LANMultiPlayer2Menu:
                    // TODO: Add your update logic here
                    MediaPlayer.Stop();
                    MainGameUpdate(GameTypes.GameType.LAN, gameTime);
                    break;

                case UIPages.UIPage.LANHMultiPlayer2Menu:
                    // TODO: Add your update logic here
                    MediaPlayer.Stop();
                    MainGameUpdate(GameTypes.GameType.LANH, gameTime);
                    break;

                case UIPages.UIPage.SinglePlayer2Menu:
                    // TODO: Add your update logic here
                    MediaPlayer.Stop();
                    MainGameUpdate(GameTypes.GameType.Practice, gameTime);
                    break;

                case UIPages.UIPage.HangarMenu:
                    hangarMenu.CheckClick(GetButtonTouch());
                    break;
                
                default:
                    if (MediaPlayer.State != MediaState.Playing)
                    {
                        mainMenu.PlayMusic(mainMusic);
                    }

                    gameState = mainMenu.CheckClick(GetButtonTouch());
                    break;
            }

            
            base.Update(gameTime);
        }

        protected override void Draw(GameTime gameTime)
        {

            GraphicsDevice.Clear(Color.Black);
            
            switch (gameState)
            {
                case UIPages.UIPage.AboutMenu:
            
                    DrawSprites(ref device);

                    aboutMenu.DrawScreen();

                    EndSprites(ref device);
            
                    break;

                case UIPages.UIPage.SettingsMenu:
            
                    DrawSprites(ref device);

                    settingsMenu.DrawScreen();

                    EndSprites(ref device);
            
                    break;

                case UIPages.UIPage.SinglePlayerMenu:
            
                    DrawSprites(ref device);

                    singleplayerMenu.DrawScreen();

                    EndSprites(ref device);
            
                    break;

                case UIPages.UIPage.MultiPlayerMenu:
            
                    DrawSprites(ref device);

                    multiplayerMenu.DrawScreen();

                    EndSprites(ref device);
            
                    break;

                case UIPages.UIPage.LANMultiPlayerMenu:

                    DrawSprites(ref device);

                    lanMultiplayerMenu.DrawScreen();

                    EndSprites(ref device);

                    break;

                case UIPages.UIPage.LANHMultiPlayerMenu:

                    DrawSprites(ref device);

                    lanhMultiplayerMenu.DrawScreen();

                    EndSprites(ref device);

                    break;

                case UIPages.UIPage.LANMultiPlayer2Menu:

                    // TODO: Add your drawing code here
                    MainGameDraw(GameTypes.GameType.LAN, gameTime);

                    break;

                case UIPages.UIPage.LANHMultiPlayer2Menu:

                    // TODO: Add your drawing code here
                    MainGameDraw(GameTypes.GameType.LANH, gameTime);

                    break;

                case UIPages.UIPage.SinglePlayer2Menu:
            
                    // TODO: Add your drawing code here
                    MainGameDraw(GameTypes.GameType.Practice, gameTime);

                    break;
            
                case UIPages.UIPage.HangarMenu:
            
                    DrawSprites(ref device);

                    hangarMenu.DrawScreen();

                    EndSprites(ref device);

                    break;

                default:

                    DrawSprites(ref device);

                    mainMenu.DrawScreen();

                    EndSprites(ref device);

                    break;
            
            }
            base.Draw(gameTime);
        }

        private void MainGameDraw(GameTypes.GameType gameType, GameTime gameTime)
        {
            DrawMap(); // Draw scene

            foreach (BulletObject bulletEntity in bulletsList)
            {
                bulletEntity.Draw(Constants.plasmaScale, 0.55f); // Draw projectiles
            }

            DrawEntity(); // Draw player

            foreach (Element element in mapDef.GetMapElements())
            {
                DrawElement(element.position, element.alpha, 1.0f, element.model);

                if (element.type == ElementTypes.ElementType.Station)
                {
                    DrawElement(element.position, 0.25f, element.radius * Constants.shieldScale, shield); // Fill in scene
                }
            }

            for (int playerNum = 0; playerNum < otherPlayers_Table.Count; playerNum++)
            {
                Dictionary<Guid, PlayerEntity>.KeyCollection otherPlayers_Keys = otherPlayers_Table.Keys;

                Guid playerKey = otherPlayers_Keys.ToList()[playerNum];

                DrawEntity(otherPlayers_Table[playerKey]); // Draw the other players

                if (otherPlayers_Table[playerKey].radius == 0.0f) otherPlayers_Table[playerKey].radius = collisionDetector.GetBoundingSphere(otherPlayers_Table[playerKey].pos, model, Constants.modelScale).Radius;

                if (otherPlayers_Table[playerKey].drawShieldFlash) DrawElement(otherPlayers_Table[playerKey].pos, 0.5f, otherPlayers_Table[playerKey].radius * 0.5f, shield, new Vector3(0.1f, 1.0f, 0.1f));
                if (otherPlayers_Table[playerKey].drawArmourFlash) DrawElement(otherPlayers_Table[playerKey].pos, 0.2f, otherPlayers_Table[playerKey].radius * 0.3f, shield, new Vector3(0.1f, 0.5f, 0.1f));
                CleanPlayer(playerKey);
            }

            DrawStarField(); // Draw the star dust

            DrawSprites(ref device);

            DrawHud(); // Draw the UI

            if (isHit)
            {
                isHitTimeSpan += gameTime.ElapsedGameTime;

                if (isHitTimeSpan.Seconds < 1)
                {
                    hud.DrawHUDHitIndicator(spriteBatch, hitindicator, device.Viewport);

                    if (player.shieldValue > 10) player.drawShieldFlash = true;
                    else player.drawArmourFlash = true;
                }
                else
                {
                    isHitTimeSpan = new TimeSpan();
                    isHit = false;
                    player.drawShieldFlash = false;
                    player.drawArmourFlash = false;
                }

            }

            EndSprites(ref device);
        }

        private void MainGameUpdate(GameTypes.GameType gameType, GameTime gameTime)
        {
            UpdateControl(gameTime);

            BulletObject tempBulletObject = new BulletObject();
            UpdateBullets(tempBulletObject);
            
            UpdateEntity(player);
            if(gameType != GameTypes.GameType.Practice && gameType != GameTypes.GameType.Campaign) UpdateNetwork(player);

            UpdateCamera();

            UpdateStarField();
        }

        #region Update methods

        private void UpdateBullets(BulletObject tempBulletObject)
        {
            for (int bulletCounter = 0; bulletCounter < bulletsList.Count; bulletCounter++)
            {
                tempBulletObject = bulletsList[bulletCounter];
                bulletsList[bulletCounter].Update();

                if (tempBulletObject.ownerId != player.playerID)
                {
                    if (collisionDetector.CheckCollision(player, tempBulletObject))
                    {
                        bulletsList[bulletCounter].terminate = true;
                        isHit = true;

                        if (tempBulletObject.bulletType == BulletObject.BulletType.plasma)
                        {
                            if (player.shieldValue > 0) player.shieldValue -= tempBulletObject.damage;
                            if (player.shieldValue < 10) player.armourValue -= tempBulletObject.damage;
                        }

                        if (player.shieldValue < 0) player.shieldValue = 0.0f;
                        if (player.armourValue < 0) player.armourValue = 0.0f;

                        if (player.shieldValue > Constants.shieldValue) player.shieldValue = Constants.shieldValue;
                        if (player.armourValue > Constants.armourValue) player.armourValue = Constants.armourValue;
                    }
                }

                if (tempBulletObject.terminate) bulletsList.RemoveAt(bulletCounter);
            }

        }

        private void UpdateControl(GameTime gameTime)
        {
            thumbsticksVals = controller.GetThumbSticksControl(gameTime);
            accelerometerVals = controller.GetAccelerometerControl(gameTime);

            float yaw = 0;
            float pitch = 0;
            float roll = 0;
            float rightSideways = 0;
            float rightUpDown = 0;
            float leftSideways = 0;

            //if (!thumbsticksVals.isRightDeadZone)
            //{
            //    // roll
            //    rightSideways = -thumbsticksVals.rightPos.X;
            //    roll = (rightSideways / 20);

            //    roll = MathHelper.Clamp(roll, -0.1f, 0.1f);

            //    thumbsticksVals.rightCentrePos.X = 0;
            //    thumbsticksVals.rightPos.X = 0;
            //    // end roll

            //    // pitch
            //    rightUpDown = thumbsticksVals.rightPos.Y;
            //    pitch = (rightUpDown / 30);

            //    if (settingsMenu.InvertY)
            //    {
            //        pitch = -1 * pitch;
            //    }

            //    pitch = MathHelper.Clamp(pitch, -0.1f, 0.1f);

            //    thumbsticksVals.rightCentrePos.Y = 0;
            //    thumbsticksVals.rightPos.Y = 0;
            //    // end pitch
            //}

            #region accelerometer

            #region roll
            rightSideways = -accelerometerVals.centrePos.Y;

            if (rightSideways < Constants.accelDeadzoneVal && rightSideways > -Constants.accelDeadzoneVal) rightSideways = 0.001f; 

            roll = (rightSideways / -30);

            roll = MathHelper.Clamp(roll, -0.1f, 0.1f);

            accelerometerVals.centrePos.Y = 0;
            #endregion

            #region pitch
            rightUpDown = accelerometerVals.centrePos.Z;
            
            rightUpDown *= 2;
            rightUpDown += 0.75f; // shift forward
            rightUpDown = MathHelper.Clamp(rightUpDown, -1.0f, 1.0f);

            pitch = (rightUpDown / 30);

            if (settingsMenu.InvertY)
            {
                pitch = -1 * pitch;
            }

            pitch = MathHelper.Clamp(pitch, -0.1f, 0.1f);

            accelerometerVals.centrePos.Z = 0;
            #endregion

            #endregion

            #region thumbsticks
            if (!thumbsticksVals.isLeftDeadZone)
            {
                #region yaw
                leftSideways = -thumbsticksVals.leftPos.X;
                yaw = (leftSideways / 20);

                yaw = MathHelper.Clamp(yaw, -0.1f, 0.1f);

                thumbsticksVals.leftCentrePos.X = 0;
                thumbsticksVals.leftPos.X = 0;
                #endregion

                #region velocity
                float velocity = 0;

                velocity = thumbsticksVals.leftPos.Y;
                velocity = (velocity / 30);

                velocity = MathHelper.Clamp(velocity, -0.1f, 0.2f);

                thumbsticksVals.leftCentrePos.Y = 0;
                thumbsticksVals.leftPos.Y = 0;

                player.velocity = -velocity;
                #endregion
            }
            #endregion

            Quaternion additionalRot = Quaternion.CreateFromAxisAngle(new Vector3(0, 1, 0), yaw) * Quaternion.CreateFromAxisAngle(new Vector3(1, 0, 0), pitch) * Quaternion.CreateFromAxisAngle(new Vector3(0, 0, 1), roll);

            player.rotQuat *= additionalRot;
           

            #region HUD
            hud.CheckClick(GetButtonTouch());
            #endregion

        }

        private void UpdateCamera()
        {
            
            Camera.avatarPosition = player.pos;
            Camera.avatarRotation = player.rotQuat;


            Camera.Update();
        }

        private void UpdateEntity(PlayerEntity player)
        {
            bool hasCollided = false;

            Quaternion rotQuat = player.rotQuat;
            Vector3 pos = player.pos;
            float speed = player.velocity;

            // element collision
            foreach (Element element in mapDef.GetMapElements())
            {
                pos = collisionDetector.CheckCollision(player, element);
                if (player.pos != pos)
                {
                    hasCollided = true;
                    break;
                }
            }

            if (!hasCollided)
            {
                Vector3 addVector = Vector3.Transform(new Vector3(0, 0, -1), rotQuat);
                pos += addVector * speed;
            }

            player.pos = pos;

            Vector3 playerNormal = new Vector3();
            Vector3 playerForward = new Vector3();
            Vector3 playerRight = new Vector3();

            playerNormal = Vector3.Transform(new Vector3(0, 1, 0), Matrix.CreateFromQuaternion(rotQuat));
            playerNormal += pos;

            playerForward = Vector3.Transform(new Vector3(0, 0, 1), Matrix.CreateFromQuaternion(rotQuat));
            playerForward += pos;

            playerRight = Vector3.Transform(new Vector3(1, 0, 0), Matrix.CreateFromQuaternion(rotQuat));
            playerRight += pos;

            playerPlaneYZ = new Plane(playerNormal, playerForward, pos);
            playerPlaneXY = new Plane(playerNormal, playerRight, pos);
        }

        #endregion

        #region Network methods

        private void UpdateNetwork(PlayerEntity player)
        {
            StringBuilder networkPacketBuilder = new StringBuilder();

            networkPacketBuilder.Clear(); // Clean the network string

            networkPacketBuilder.Append("POSN:");
            networkPacketBuilder.Append(player.playerID);
            networkPacketBuilder.Append('|');
            networkPacketBuilder.Append(player.pos.X);
            networkPacketBuilder.Append('|');
            networkPacketBuilder.Append(player.pos.Y);
            networkPacketBuilder.Append('|');
            networkPacketBuilder.Append(player.pos.Z);
            networkPacketBuilder.Append('|');
            networkPacketBuilder.Append(player.rotQuat.X);
            networkPacketBuilder.Append('|');
            networkPacketBuilder.Append(player.rotQuat.Y);
            networkPacketBuilder.Append('|');
            networkPacketBuilder.Append(player.rotQuat.Z);
            networkPacketBuilder.Append('|');
            networkPacketBuilder.Append(player.rotQuat.W);
            networkPacketBuilder.Append('|');
            if (player.drawShieldFlash) networkPacketBuilder.Append(1);
            else networkPacketBuilder.Append(0);
            networkPacketBuilder.Append('|');
            if (player.drawArmourFlash) networkPacketBuilder.Append(1);
            else networkPacketBuilder.Append(0);

            peerNode.Send(networkPacketBuilder.ToString());
        }

        private static void UpdateNetwork(BulletObject bullet)
        {
            StringBuilder networkPacketBuilder = new StringBuilder();

            networkPacketBuilder.Clear(); // Clean the network string

            networkPacketBuilder.Append("BULT:");
            networkPacketBuilder.Append(bullet.ownerId);
            networkPacketBuilder.Append('|');
            networkPacketBuilder.Append(bullet.pos.X);
            networkPacketBuilder.Append('|');
            networkPacketBuilder.Append(bullet.pos.Y);
            networkPacketBuilder.Append('|');
            networkPacketBuilder.Append(bullet.pos.Z);
            networkPacketBuilder.Append('|');
            networkPacketBuilder.Append(bullet.rot.X);
            networkPacketBuilder.Append('|');
            networkPacketBuilder.Append(bullet.rot.Y);
            networkPacketBuilder.Append('|');
            networkPacketBuilder.Append(bullet.rot.Z);
            networkPacketBuilder.Append('|');
            networkPacketBuilder.Append(bullet.rot.W);
            networkPacketBuilder.Append('|');
            networkPacketBuilder.Append(bullet.bulletType);
            networkPacketBuilder.Append('|');
            networkPacketBuilder.Append(bullet.damage);
            networkPacketBuilder.Append('|');
            networkPacketBuilder.Append(bullet.velocity);

            peerNode.Send(networkPacketBuilder.ToString());
        }

        #endregion

        #region Draw methods

        private void DrawMap()
        {
            //clear and lock depth buffer since the skysphere is the furthest object
            device.DepthStencilState = DepthStencilState.None;
            SamplerState samplerState = device.SamplerStates[0];
            device.SamplerStates[0] = SamplerState.LinearClamp;

            worldMatrix = Matrix.CreateScale(0.01f, 0.01f, 0.01f) * Matrix.CreateTranslation(player.pos);
            //worldMatrix = Matrix.CreateScale(0.001f, 0.001f, 0.001f) * Matrix.CreateRotationY(MathHelper.Pi) * Matrix.CreateTranslation(player.pos);
            //worldMatrix = Matrix.CreateScale(0.0001f, 0.0001f, 0.0001f) * Matrix.CreateTranslation(player.pos);

            // Copy any parent transforms.
            Matrix[] transforms = new Matrix[skysphere.Bones.Count];
            skysphere.CopyAbsoluteBoneTransformsTo(transforms);

            // Draw the model. A model can have multiple meshes, so loop.
            foreach (ModelMesh mesh in skysphere.Meshes)
            {
                // This is where the mesh orientation is set, as well
                // as our camera and projection.
                foreach (BasicEffect effect in mesh.Effects)
                {
                    effect.EnableDefaultLighting();
                    effect.View = Camera.view;
                    effect.World = transforms[mesh.ParentBone.Index] * worldMatrix;
                    effect.Projection = Camera.projection;
                }
                // Draw the mesh, using the effects set above.
                mesh.Draw();
            }
            device.DepthStencilState = DepthStencilState.Default;
            device.SamplerStates[0] = samplerState;
            //device.Clear(ClearOptions.DepthBuffer, Color.Black, 1.0f, 0);
        }

        private void DrawEntity()
        {
            worldMatrix = Matrix.CreateScale(Constants.modelScale, Constants.modelScale, Constants.modelScale) * Matrix.CreateRotationY(MathHelper.Pi) * Matrix.CreateFromQuaternion(player.rotQuat) * Matrix.CreateTranslation(player.pos);
            //worldMatrix = Matrix.CreateScale(0.001f, 0.001f, 0.001f) * Matrix.CreateRotationY(MathHelper.Pi) * Matrix.CreateTranslation(player.pos);
            //worldMatrix = Matrix.CreateScale(0.0001f, 0.0001f, 0.0001f) * Matrix.CreateTranslation(player.pos);

            // Copy any parent transforms.
            Matrix[] transforms = new Matrix[model.Bones.Count];
            model.CopyAbsoluteBoneTransformsTo(transforms);

            // Draw the model. A model can have multiple meshes, so loop.
            foreach (ModelMesh mesh in model.Meshes)
            {
                // This is where the mesh orientation is set, as well
                // as our camera and projection.
                foreach (BasicEffect effect in mesh.Effects)
                {
                    effect.EnableDefaultLighting();
                    effect.View = Camera.view;
                    effect.World = transforms[mesh.ParentBone.Index] * worldMatrix;
                    effect.Projection = Camera.projection;
                }
                // Draw the mesh, using the effects set above.
                mesh.Draw();

            }

            //if (player.collisionSphere.Radius > 0)
            //{
            //    player.collisionSphere = collisionDetector.UpdateBoundingSphere(player.pos, player.collisionSphere.Radius);
            //}
            //else
            //{
            //    player.collisionSphere = collisionDetector.GetBoundingSphere(player.pos, model, modelScale);
            //}

            if (player.collisionBox != null)
            {
                player.collisionBox = collisionDetector.UpdateBoundingBox(player.collisionBox, worldMatrix);
            }
            else
            {
                player.collisionBox = collisionDetector.GetBoundingBox(transforms, model, Constants.modelScale);
            }
            
            
        }

        private void DrawEntity(PlayerEntity otherPlayer)
        {
            worldMatrix = Matrix.CreateScale(Constants.modelScale, Constants.modelScale, Constants.modelScale) * Matrix.CreateRotationY(MathHelper.Pi) * Matrix.CreateFromQuaternion(otherPlayer.rotQuat) * Matrix.CreateTranslation(otherPlayer.pos);
            //worldMatrix = Matrix.CreateScale(0.001f, 0.001f, 0.001f) * Matrix.CreateRotationY(MathHelper.Pi) * Matrix.CreateTranslation(player.pos);
            //worldMatrix = Matrix.CreateScale(0.0001f, 0.0001f, 0.0001f) * Matrix.CreateTranslation(player.pos);

            // Copy any parent transforms.
            Matrix[] transforms = new Matrix[model.Bones.Count];
            model.CopyAbsoluteBoneTransformsTo(transforms);

            // Draw the model. A model can have multiple meshes, so loop.
            foreach (ModelMesh mesh in model.Meshes)
            {
                // This is where the mesh orientation is set, as well
                // as our camera and projection.
                foreach (BasicEffect effect in mesh.Effects)
                {
                    effect.EnableDefaultLighting();
                    effect.View = Camera.view;
                    effect.World = transforms[mesh.ParentBone.Index] * worldMatrix;
                    effect.Projection = Camera.projection;
                }
                // Draw the mesh, using the effects set above.
                mesh.Draw();

            }

            //if (player.collisionSphere.Radius > 0)
            //{
            //    player.collisionSphere = collisionDetector.UpdateBoundingSphere(player.pos, player.collisionSphere.Radius);
            //}
            //else
            //{
            //    player.collisionSphere = collisionDetector.GetBoundingSphere(player.pos, model, modelScale);
            //}

            if (otherPlayer.collisionBox != null)
            {
                otherPlayer.collisionBox = collisionDetector.UpdateBoundingBox(otherPlayer.collisionBox, worldMatrix);
            }
            else
            {
                otherPlayer.collisionBox = collisionDetector.GetBoundingBox(transforms, model, Constants.modelScale);
            }


        }

        private void DrawElement(Vector3 position, float alpha, float elementScale, Model elementModel)
        {
            DrawElement(position, alpha, elementScale, elementModel, new Vector3(1.0f, 1.0f, 1.0f));
        }

        private void DrawElement(Vector3 position, float alpha, float elementScale, Model elementModel, Vector3 emissiveColor)
        {
            worldMatrix = Matrix.CreateScale(elementScale, elementScale, elementScale) * Matrix.CreateTranslation(position);

            // Copy any parent transforms.
            Matrix[] transforms = new Matrix[elementModel.Bones.Count];
            elementModel.CopyAbsoluteBoneTransformsTo(transforms);

            // Draw the model. A model can have multiple meshes, so loop.
            foreach (ModelMesh mesh in elementModel.Meshes)
            {
                // This is where the mesh orientation is set, as well
                // as our camera and projection.
                foreach (BasicEffect effect in mesh.Effects)
                {
                    effect.EnableDefaultLighting();
                    effect.View = Camera.view;
                    effect.World = transforms[mesh.ParentBone.Index] * worldMatrix;
                    effect.Projection = Camera.projection;
                    effect.Alpha = alpha;
                    effect.EmissiveColor = emissiveColor;
                }
                // Draw the mesh, using the effects set above.
                mesh.Draw();

            }
        }

        private void DrawHud()
        {
            controller.DrawControls(spriteBatch, thumbstick);
            hud.DrawHUDReticule(spriteBatch, reticule, device.Viewport);
            hud.DrawHUDText(textBatch, "X: " + player.pos.X.ToString() + " Y : " + player.pos.Y.ToString() + " Z : " + player.pos.Z.ToString(), new Vector2(1, 1));
            hud.DrawHUDText(textBatch, "Shield: " + player.shieldValue.ToString() + " / Armour: " + player.armourValue.ToString(), new Vector2(1, 10));
            hud.DrawHUDUI(spriteBatch, firebutton, new Vector2(device.Viewport.Width - 100, device.Viewport.Height - 100));
            hud.DrawHUDShieldBar(uiBatch, valuebar, device.Viewport, player.shieldValue, Constants.shieldValue);
            hud.DrawHUDArmourBar(uiBatch, valuebar, device.Viewport, player.armourValue, Constants.armourValue);
        }

        #endregion

        #region Sprites methods

        private void DrawSprites(ref GraphicsDevice sbDevice)
        {
            textBatch.Begin(SpriteSortMode.Texture, BlendState.AlphaBlend);
            spriteBatch.Begin(SpriteSortMode.Texture, BlendState.AlphaBlend);
            uiBatch.Begin(SpriteSortMode.Texture, BlendState.AlphaBlend);
        }

        private void EndSprites(ref GraphicsDevice sbDevice)
        {
            textBatch.End();
            spriteBatch.End();
            uiBatch.End();

            RasterizerState rasterizer = new RasterizerState();
            rasterizer.CullMode = CullMode.None;

            device.DepthStencilState = DepthStencilState.Default;
            device.RasterizerState = rasterizer;

            SamplerState samplerState = new SamplerState();
            samplerState.AddressU = TextureAddressMode.Wrap;
            samplerState.AddressV = TextureAddressMode.Wrap;

            device.SamplerStates[0] = samplerState;
        }

        #endregion

        #region Map methods

        private void PopulateMapList()
        {
            MapLoader mapLoader = new MapLoader();

            mapList = mapLoader.LoadMaps(this.Content);
        }

        private void LoadMap(int mapIndex)
        {
            string path = mapList[mapIndex].relPath;
            Model mapModel = Content.Load<Model>(path);

            mapList[mapIndex].map = mapModel;

        }

        #endregion

        #region Misc methods

        private Vector2 GetButtonTouch()
        {
            Vector2 touchscreenInput = new Vector2();

            TouchCollection touches = TouchPanel.GetState();

            foreach (var touch in touches)
            {
                touchscreenInput = touch.Position;
            }

            return touchscreenInput;
        }

        private void CleanPlayer(Guid playerNum)
        {
            if (!otherPlayers_Table[playerNum].isNPC)
            {
                if (DateTime.Now - otherPlayers_Table[playerNum].lastUpdated > TimeSpan.FromSeconds(30))
                {
                    otherPlayers_Table.Remove(playerNum);
                }
            }
        }

        public static void UpdatePlayer(PlayerEntity newPlayer)
        {
            if (otherPlayers_Table.ContainsKey(newPlayer.playerID))
            {
                otherPlayers_Table[newPlayer.playerID] = newPlayer;
            }
            else
            {
                otherPlayers_Table.Add(newPlayer.playerID, newPlayer);
            }
        }

        public static void UpdateBullet(BulletObject.BulletType bulletType)
        {
            BulletObject newBullet = new BulletObject();

            switch (bulletType)
            {
                case BulletObject.BulletType.plasma:
                    newBullet.Init(player.playerID, bullet_Plasma, player.pos, player.rotQuat, 0.2f + player.velocity, bulletType, Constants.plasmaDamage);
                    break;
            }

            // Since it is ourselves (same player), send over network the details of the bullet
            UpdateNetwork(newBullet);

            bulletsList.Add(newBullet);
        }

        public static void UpdateBullet(BulletObject newBullet)
        {
            switch (newBullet.bulletType)
            {
                case BulletObject.BulletType.plasma:
                    newBullet.Init(newBullet.ownerId, bullet_Plasma, newBullet.pos, newBullet.rot, newBullet.velocity, newBullet.bulletType, newBullet.damage);
                    break;
            }

            bulletsList.Add(newBullet);
        }

        #endregion
    }
}
